/*
 * MIT License
 *
 * Copyright (c) 2020 Mr.css
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */

package cn.seaboot.openapi;

import cn.seaboot.commons.core.CommonUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nullable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * hibernate-validator 序列化工具
 * <p>
 * 已经清除相关依赖，目前没用上
 * <p>
 *
 * @author Mr.css
 * @version 2022-09-30 16:36
 */
public class ValidatorUtils {
    private static final Logger logger = LoggerFactory.getLogger(ValidatorUtils.class);

    /**
     * 序列化 hibernate-validator 时，忽略这些参数字段
     */
    private static final String[] transientField = {"message", "groups", "payload"};

    /**
     * 扫描参数所包含的 hibernate-validator
     *
     * @param group     分组
     * @param parameter 参数对象
     * @return 序列化之后的 hibernate-validator
     */
    public static List<Map<String, Object>> getParameterValidator(Class<?>[] group, Parameter parameter) {
        return getValidator(group, parameter.getAnnotations());
    }

    /**
     * 扫描字段所包含的 hibernate-validator
     *
     * @param group 分组
     * @param field 字段对象
     * @return 序列化之后的 hibernate-validator
     */
    public static List<Map<String, Object>> getFieldValidator(Class<?>[] group, Field field) {
        return getValidator(group, field.getAnnotations());
    }

    /**
     * 扫描 hibernate-validator
     *
     * @param group       分组
     * @param annotations 注解数组
     * @return 序列化之后的 hibernate-validator
     */
    public static List<Map<String, Object>> getValidator(Class<?>[] group, Annotation[] annotations) {
        List<Map<String, Object>> annotationList = new ArrayList<>();
        for (Annotation annotation : annotations) {
            Map<String, Object> params = ValidatorUtils.serializeValidator(group, annotation);
            if (params != null) {
                annotationList.add(params);
            }
        }
        return annotationList;
    }

    /**
     * 序列化 hibernate-validator 相关的注解
     *
     * @param group      分组
     * @param annotation 注解
     * @return 如果不是 hibernate-validator 的注解，直接忽略
     */
    @Nullable
    public static Map<String, Object> serializeValidator(Class<?>[] group, Annotation annotation) {
        Class<?> annotationType = annotation.annotationType();
        String pkg = annotationType.getName();
        //如果注解是 hibernate.validator 或者 javax.validation
        if (pkg.startsWith("org.hibernate.validator.constraints")
                || pkg.startsWith("jakarta.validation.constraints")
                || pkg.startsWith("javax.validation.constraints")) {
            try {
                // 获取 validator 分组
                Method m = annotationType.getMethod("groups");
                Class<?>[] fieldGroup = (Class<?>[]) m.invoke(annotation);
                if (isHitValidator(group, fieldGroup)) {
                    Method[] methods = annotationType.getDeclaredMethods();
                    Map<String, Object> map = new HashMap<>(methods.length);

                    //将注解名称作为类型
                    map.put("_type", annotationType.getSimpleName());
                    for (Method method : methods) {
                        try {
                            String methodName = method.getName();
                            //忽略非必要的参数，减少数据量
                            if (isTransientField(methodName)) {
                                map.put(method.getName(), method.invoke(annotation));
                            }
                        } catch (IllegalAccessException | InvocationTargetException e) {
                            //如果无法解析，则直接忽略
                            logger.error("无法使用Java反射解析响应数据：", e);
                        }
                    }
                    return map;
                }
            } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                logger.debug("reflect exceptions: ", e);
            }
        }
        return null;
    }

    /**
     * 序列化的时候，忽略某些字段
     * <p>
     * Ignore some useless validator field
     *
     * @param fieldName -
     * @return boolean
     */
    private static boolean isTransientField(String fieldName) {
        for (String str : transientField) {
            if (str.equals(fieldName)) {
                return false;
            }
        }
        return true;
    }

    /**
     * 分组是否命中
     * <p>
     * 1、如果双方都没配置分组，校验规则生效；
     * 2、只有一方配置了分组，校验规则不生效；
     * 3、组别配置命中，校验规则生效
     * <p>
     * 检测同组的 validator，同组的 validator 才会触发校验
     * <p>
     * hibernate-validator configuration.
     * Compare validate group, if in the same group, will get description form hibernate-validator.
     *
     * @param group1 methodGroup
     * @param group2 fieldGroup
     * @return result
     */
    public static boolean isHitValidator(Class<?>[] group1, Class<?>[] group2) {
        if (CommonUtils.isEmpty(group1)) {
            // 都不指定分组的话，校验功能生效
            return CommonUtils.isEmpty(group2);
        } else {
            if (CommonUtils.isEmpty(group2)) {
                // 一个指定分组，一个不指定，则不生效
                return false;
            } else {
                // 都指定了分组的情况，必须完全匹配才能生效
                for (Class<?> g1 : group1) {
                    for (Class<?> g2 : group2) {
                        if (g1.equals(g2)) {
                            return true;
                        }
                    }
                }
                return false;
            }
        }
    }
}
